package jpa4azure.type;

import static jpa4azure.type.PrimitiveWrapper.isPrimitive;

import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.util.Collection;
import java.util.Date;
import java.util.UUID;

import javax.persistence.PersistenceException;

import com.windowsazure.samples.Property;
import com.windowsazure.samples.PropertyCollection;


public class FieldContext {

	private static final String INDEX = "_INDEX";
	protected Field field;
	public Object subject;
	

	public FieldContext(Object o, Field field) {
		this.field = field;
		field.setAccessible(true);
		subject = o;
	}

	public boolean isSimpleType() {
		return isPrimitive(field.getType()) || arrayPrimitive();
	}

	private boolean arrayPrimitive() {
		return field.getType().isArray()
				&& PrimitiveWrapper.isPrimitive(field.getType()
						.getComponentType());
	}

	public Object value() {
		Object result = null;
		try {
			result = field.get(subject);
		} catch (Exception e) {
			Utils.runtime(e);
		}
		if (result == null)
			return result;
		else if (isPlural())
			return ((Collection) result).toArray();
		else
			return result;
	}

	public Collection<Object> values() {
		try {
			return (Collection) field.get(subject);
		} catch (Exception e) {
			Utils.runtime(e);
		}
		return null;
	}

	public String name() {
		return field.getName();
	}

	public boolean isSingular() {
		return !field.getType().isAssignableFrom(Collection.class);
	}

	public Class<?> type() {
		return field.getType();
	}

	public boolean isPlural() {
		return field.getType().isAssignableFrom(Collection.class);
	}

	public boolean isPluralPrimitive() {
		return isPlural() && isPrimitive(type2());
	}
	
	public boolean isPluralComplex() {
		return isPlural() && !isPrimitive(type2());
	}

	public Class<?> type2() {
		return getGenericType((ParameterizedType) field.getGenericType());
	}

	private Class<?> getGenericType(ParameterizedType type) {
		return (type == null) ? NullType.class : (Class<?>) type
				.getActualTypeArguments()[0];
	}

	public String getIndexName() {
		return subject.getClass().getName() + '.' + field.getName() + INDEX;
	}

	public String getFieldname() {
		return field.getName();
	}

	
    public void	getCloudTableColumn(PropertyCollection values) throws Exception { 
		String val = null;
		if ( value() == null)
			return;
		val = value().toString();
		values.add(getProperty());
	}
	
	
	/**
	 * Get the field type by given field
	 * @param f
	 *        the Field object
	 * @return an ETableColumnType object
	 */
	public Property getProperty() {
		Class c = field.getType();
		if (c == int.class || c == Integer.class || c == byte.class
				|| c == Byte.class) {
			return Property.newProperty(getFieldname(), (Integer)value());
		} else if (c == long.class || c == Long.class ) {
			return Property.newProperty(getFieldname(), (Long)value());
		} else if ( c == double.class || c == Double.class ) {
			return Property.newProperty(getFieldname(), (Double)value());
		} else if (c == Date.class) {
			return Property.newProperty(getFieldname(), (Date)value());
		} else if (c == byte[].class) {
			return Property.newProperty(getFieldname(), (Long)value());
		} else if (c == boolean.class || c == Boolean.class) {
			return Property.newProperty(getFieldname(), (Boolean)value());
		} else if (c == UUID.class) {
			return Property.newProperty(getFieldname(), (UUID)value());
		} else if (c == String.class ) {
			return Property.newProperty(getFieldname(), (String)value());
		} else {
			throw new PersistenceException("Cannot persist field " + field.getName() + " of type " + type().getName());
		}
	}

	public void setProperty(Object value) {
		try {
			if (value instanceof Double)
				value = ((Double)value).floatValue();
			field.set(subject, value);
		} catch (IllegalArgumentException e) {
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			e.printStackTrace();
		}	
	}


	
}
